﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.IO;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Threading;
using System.Threading;
using System.Reflection;
using FinPlusAssembler;
using FinPlusInterfaces;

namespace FinPlusCompView
{
    public partial class FinPlusComponentTester : FinPlusControl
    {
        [State(Test = true)]
        public override bool IsValid { get; set; }
        [State(Save = true)]
        public new double Height { get { return base.Height; } set { base.Height = value; } }
        [State(Save = true)]
        public new double Width { get { return base.Width; } set { base.Width = value; } }
        [State(Save = true, Test = true)]
        public string TestPath { get { return _testPath; } set { tFilePath.Text = _testPath = value; } }

        private const string NotepadShortcut = @"notepad.lnk", XmlpadShortcut = @"xmlpad.lnk", DeleteFormat = ".yyyyMMdd_hhmmss_fff";
        private TestElements _testElements = new TestElements();
        private string _path, _testPath, _testSelected;

        //construct
        public FinPlusComponentTester()
        {
            InitializeComponent();
            var g = new ListCollectionView(_testElements);
            g.GroupDescriptions.Add(new PropertyGroupDescription("TestName"));
            dataGridTests.ItemsSource = g;
            _path = Assembly.GetEntryAssembly().Path();
            IsValid = true;
        }

        //common control interface
        public override void CmdExecute(IPod command)
        {
            try
            {
                CmdCapture(command);

                switch ((Cmds)Enum.Parse(typeof(Cmds), command.Name, true))
                {
                    case Cmds.Folder: FolderLoad(command["Tag"].ToString()); break;
                    case Cmds.RunTest: RunTest(); break;
                    case Cmds.RunView: RunView(); break;
                    case Cmds.RunAndCheck: RunAndCheck(); break;
                    case Cmds.Mock: Mock(); break;
                    case Cmds.RecordEvents: RecordEvents(); break;
                    case Cmds.SyncResult: SyncResult(); break;
                    case Cmds.CheckResult: CheckResult(); break;
                    case Cmds.Reset: _testElements.Reset(); break;
                    case Cmds.Refresh: Refresh(); break;
                    case Cmds.Clear: _testElements.Clear(); break;
                    case Cmds.RunAll: RunAll(); break;
                    case Cmds.TestAll: TestAll(); break;
                    case Cmds.Xmlpad: Xmlpad(command["Tag"].ToString()); break;
                    case Cmds.Notepad: Notepad(command["Tag"].ToString()); break;
                    case Cmds.CloneTest: CloneTest(); break;
                    case Cmds.EraseTest: EraseTest(); break;
                    case Cmds.EraseFile: EraseFile(command["Tag"].ToString()); break;
                    case Cmds.SvnDiff: SvnDiff(command["Tag"].ToString()); break;
                    case Cmds.SvnAdd: SvnAdd(command["Tag"].ToString()); break;
                    case Cmds.SvnCommit: SvnCommit(command["Tag"].ToString()); break;
                    default: throw new Exception(string.Format("cmd not recognised {0}", command.Name));
                }
            }
            catch (CmdExecuteException e)
            {
                IsValid = Level.Warning.Log(Id, Config, e.Message, e);
            }
        }

        //private
        private void FolderLoad(string testPath)
        {
            tFilePath.Text = _testPath = testPath;
            Refresh();
        }

        private void RunTest(string testName = null)
        {
            try
            {
                if(string.IsNullOrEmpty(testName))
                    testName = _testSelected;

                var testType = _testElements.TestType(testName);
                if (testType == FileType.view)
                    TestView(testName);
                else if (testType == FileType.service)
                    TestService(testName);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void RunAndCheck()
        {
            try
            {
                var testName = _testSelected;
                RunTest(testName);
                CheckResult(testName);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void RunView()
        {
            try
            {
                 var thread = new Thread(new ThreadStart(() =>
                 {
                    var name = _testSelected;
                    var testType = _testElements.TestType(name);
                    if (testType != FileType.view)
                        return;

                    var view = _testElements[name, FileType.view];
                    var data = _testElements[name, FileType.data];
                    var test = IOC.New<IPods>(view.FilePath).Construct(view.Type, Env.test);

                    var states = _testElements[name, FileType.state];
                    if (states != null) 
                        test.InjectState(states.FilePath);

                    var clientWindow = new FinPlusWindow(view.FilePath);
                    clientWindow.Dock(test.Object, view.Type);
                    clientWindow.Show();
                    Dispatcher.Run();
                  }));

                  thread.SetApartmentState(ApartmentState.STA);
                  thread.IsBackground = true;
                  thread.Start();
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void TestService(string testName)
        {
            try
            {
                var thread = new Thread(new ThreadStart(() =>
                {
                    var service = _testElements[testName, FileType.service];
                    var data = _testElements[testName, FileType.data];
                    var expected = _testElements[testName, FileType.expected];
                    var result = _testElements.Clone(service, FileType.result, _testPath);
                    expected.FirstDiff = result.FirstDiff = service.FirstDiff = 0;

                    var serviceObj = (IFinPlusComp)IOC.New<IPods>(service.FilePath).Construct( service.Type, Env.test).Object;
                    serviceObj.Adapter.ToXmlDoc().Save(result.FilePath);

                    result.LastWrite = DateTime.Now;
                    service.FirstDiff = -1;
                    Dispatcher.Run();
                }));

                thread.SetApartmentState(ApartmentState.STA);
                thread.IsBackground = true;
                thread.Start();
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void TestView(string name)
        {
            try
            {
                var view = _testElements[name, FileType.view];
                var cmds = _testElements[name, FileType.cmds];
                var expected = _testElements[name, FileType.expected];
                var result = _testElements.Clone(view, FileType.result, _testPath);
                expected.FirstDiff = result.FirstDiff = view.FirstDiff = 0;

                var thread = new Thread(new ThreadStart(() =>
                {
                    var test = IOC.New<IPods>(view.FilePath).Construct(view.Type, Env.test);
                    test.Render();

                    if (cmds != null)
                        test.CmdsExecute(IOC.New<IPods>(cmds.FilePath));

                    var states = test.ExtractState(Environment.UserName, true);
                    states.ToXmlDoc().Save(result.FilePath);

                    result.LastWrite = DateTime.Now;
                    view.FirstDiff = -1;
                    Dispatcher.Run();
                }));

                thread.SetApartmentState(ApartmentState.STA);
                thread.IsBackground = true;
                thread.Start();
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void Mock()
        {
            try
            {
                var query = _testElements[_testSelected, FileType.query];
                query.FirstDiff = 0;
                var obj = (FinPlusConnectService)IOC.New<IPods>(query.FilePath).Construct( query.Type, Env.test).Object;
  
                var data = _testElements.Clone(query, FileType.data, _testPath);
                obj.Pods().ToXmlDoc().Save(data.FilePath);
                data.LastWrite = DateTime.Now;
                query.FirstDiff = -1;
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void RecordEvents()
        {
            try
            {
                var testName = _testSelected;
                if (_testElements.TestType(testName) != FileType.view) 
                    return;

                var view = _testElements[testName, FileType.view];
                var cmds = _testElements.Clone(view, FileType.cmds, _testPath);
                cmds.FirstDiff = 0;

                var viewObj = (FinPlusControl)IOC.New<IPods>(view.FilePath).Construct(view.Type, Env.test).Object;
                viewObj.Commands = IOC.New<IPods>();

                var _finPlusWindow = new FinPlusWindow(view.FilePath);
                _finPlusWindow.Dock(viewObj, null);
                _finPlusWindow.ShowDialog();

                viewObj.Commands.ToXmlDoc().Save(cmds.FilePath);
                viewObj.Commands = null;
                cmds.FirstDiff = -1;
                cmds.LastWrite = DateTime.Now;
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void SyncResult()
        {
            try
            {
                var result = _testElements[_testSelected, FileType.result];
                var expected = _testElements.Clone(result, FileType.expected, _testPath);
                
                File.Copy(result.FilePath, expected.FilePath, true);
                expected.LastWrite = DateTime.Now;
                result.FirstDiff = 0;
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void CloneTest()
        {
            try
            {
                var name = _testElements.NextTestName(_testSelected);
                var configsToClone = _testElements.Where(f => f.TestName == _testSelected).ToList();

                foreach (var configToClone in configsToClone)
                {
                    string path = string.Format("{0}\\{1}.{2}.{3}", _testPath, name, configToClone.Type, configToClone.FileType);
                    File.Copy(configToClone.FilePath, path, true);
                    _testElements.Set(path);
                }
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void EraseTest()
        {
            try
            {
                var testName = _testSelected;
                if (string.IsNullOrEmpty(testName)) 
                    return;

                var configsToDelete = _testElements.Where(f => f.TestName == testName).ToList();
                string d = DateTime.Now.ToString(DeleteFormat);

                foreach (var configToDelete in configsToDelete)
                {
                    _testElements.Remove(configToDelete);
                    File.Move(configToDelete.FilePath, configToDelete.FilePath + d);
                }
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void EraseFile(string filePath)
        {
            try
            {
                File.Move(filePath, filePath + DateTime.Now.ToString(DeleteFormat));
                _testElements.Remove(filePath);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void CheckResult(string name = null)
        {
            if(string.IsNullOrEmpty(name))
                name = _testSelected;

            StreamReader fileResult = null, fileExpected = null;

            string result = string.Empty;
            string expected = string.Empty;
            string lineDiff = string.Empty;

            try
            {
                var fileRes = _testElements[name, FileType.result];
                var podsRes = IOC.New<IPods>(fileRes.FilePath);

                var fileExp = _testElements[name, FileType.expected];
                var podsExp = IOC.New <IPods>(fileExp.FilePath);
                fileExp.FirstDiff = fileRes.FirstDiff = 0;

                int x = podsExp.Count();
                int r = podsRes.Count();

                if (x != r)
                    fileExp.FirstDiff = fileRes.FirstDiff = x;

                for (int i = 1; i <= x && i <= r; i++)
                {
                    if ( podsRes[i].IsEqual(podsExp[i]))
                        continue;

                    fileExp.FirstDiff =  fileRes.FirstDiff = i;
                    break;
                }
                
                if(fileExp.FirstDiff == 0)
                    fileExp.FirstDiff = fileRes.FirstDiff = -1;

            }
            catch (Exception e)
            {
                IsValid =  Level.Error.Log(Id, Config, e.Message, e);
            }
            finally
            {
                if (fileResult != null) 
                    fileResult.Close();
                if (fileExpected != null) 
                    fileExpected.Close();
            }
        }

        private void Refresh()
        {
            _testElements.Clear();
            if (string.IsNullOrEmpty(_testPath))
                return;

            var f = new List<string>();
            foreach (var ct in Enum.GetValues(typeof(FileType)))
                f.Add(string.Format("*.{0}", ct.ToString()));

            var files = f.SelectMany(t => Directory.GetFiles(_testPath, t, SearchOption.TopDirectoryOnly)).ToArray();
            _testElements.Add(files);
        }

        private void RunAll()
        {
            foreach (var testName in _testElements.TestNames())
                RunTest(testName);
        }

        private void TestAll()
        {
            foreach (var testName in _testElements.TestNames())
                CheckResult(testName);
        }

        private void Xmlpad(string filePath)
        {
            try
            {
                Process.Start(_path + XmlpadShortcut, filePath);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void Notepad(string filePath)
        {
            try
            {
                Process.Start(_path + NotepadShortcut, filePath);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void SvnDiff(string filePath)
        {
            try
            {
                Process.Start(_path + "TortoiseMerge.lnk", filePath);
                if (string.IsNullOrEmpty(filePath)) 
                    return;
                Process.Start("CMD.exe", @"/k svn diff " + filePath);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void SvnAdd(string filePath)
        {
            try
            {
                if(string.IsNullOrEmpty(filePath)) 
                    return;
                Process.Start("CMD.exe", @"/k svn add " + filePath);
            }
            catch (Exception e)
            {
                IsValid =  Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void SvnCommit(string filePath)
        {
            try
            {
                if (string.IsNullOrEmpty(filePath))
                    return;
                Process.Start("CMD.exe", @"/k svn commit " + filePath);
            }
            catch (Exception e)
            {
                IsValid = Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        private void Folder()
        {
            try
            {
                var d = new System.Windows.Forms.FolderBrowserDialog();
                var res = d.ShowDialog();
                if (res == System.Windows.Forms.DialogResult.OK)
                    CmdRouter(Cmds.Folder.ToString(), d.SelectedPath);
            }
            catch (Exception e)
            {
                Level.Error.Log(Id, Config, e.Message, e);
            }
        }

        //cmds
        private enum Cmds { Folder, RunView, RunTest, RunAndCheck, RunVisual, Mock, RecordEvents, SyncResult, CloneTest, CheckResult, Reset, Refresh, Clear, RunAll, TestAll, Xmlpad, Notepad, EraseTest, EraseFile, SvnDiff, SvnAdd, SvnCommit }

        //events
        private void Folder_OnClick(object s, RoutedEventArgs a) { Folder(); }
        private void Xmlpad_OnClick(object s, RoutedEventArgs a) { CmdRouter(Cmds.Xmlpad.ToString(), s); }
        private void Notepad_OnClick(object s, RoutedEventArgs a) { CmdRouter(Cmds.Notepad.ToString(), s); }
        private void EraseFile_OnClick(object s, RoutedEventArgs a) { CmdRouter(Cmds.EraseFile.ToString(), s); }
        private void SvnDiff_OnClick(object s, RoutedEventArgs a) { CmdRouter(Cmds.SvnDiff.ToString(), s); }
        private void SvnAdd_OnClick(object s, RoutedEventArgs a) { CmdRouter(Cmds.SvnAdd.ToString(), s); }
        private void SvnCommit_OnClick(object s, RoutedEventArgs a) { CmdRouter(Cmds.SvnCommit.ToString(), s); }
        private void Group_OnFocus(object s, RoutedEventArgs e) {_testSelected = ((ToolBar)s).Tag.ToString(); }
    }
}
